#pragma once

#include "common.hpp"
#include "log.hpp"

namespace win {
using handle = ::HANDLE;
using socket = ::SOCKET;
using overlapped = ::OVERLAPPED;

using socket = ::SOCKET;
using sockaddr_in = ::SOCKADDR_IN;

using wsa_buf = ::WSABUF;

using dword = ::DWORD;
using ulong = ::ULONG;
using ulong_ptr = ::ULONG_PTR;

using iocp_object = ::TP_IO;
using tp_callback_instance = ::TP_CALLBACK_INSTANCE;

inline std::string get_error_message(dword dwErr) {
    WCHAR wszMsgBuff[512];
    DWORD dwChars = FormatMessage(
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, dwErr, 0, wszMsgBuff, 512, nullptr);
    if (dwChars == 0) {
        HINSTANCE hInst = LoadLibrary(L"Ntdsbmsg.dll");
        if (!hInst) {
            return "Error message not found.";
        }
        // Try getting message text from ntdsbmsg.
        dwChars = FormatMessage(
            FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, hInst, dwErr, 0, wszMsgBuff, 512, nullptr);
        FreeLibrary(hInst);
    }
    std::wstring ws = dwChars ? wszMsgBuff : L"Error message not found.";
    return nonstd::to_string(ws);
}

inline std::string get_last_error_message() {
    auto code = ::GetLastError();
    return std::to_string(code) + ": " + get_error_message(code);
}

inline void initialize_socket_library() {
    WSAData wsa_data{};
    int result = ::WSAStartup(MAKEWORD(2, 2), &wsa_data);
    assert(!result, "WSAStartup failed: " << get_last_error_message());
}
} // namespace win